package de.saar.penguin.give2.server;

import give.formula.Atom;
import give.formula.Orientation;
import give.formula.Position;
import give.formula.Term;
import give.util.GeometryTools;
import java.awt.geom.*;
import give.world.*;

import java.util.*;
import give.nlgserver.discretize.*;
import java.io.IOException;
import java.util.ArrayList;

import org.jgrapht.alg.*;
import org.jgrapht.*;
import org.jgrapht.graph.*;

/**
 * @author markus
 * 
 * This class is intended to contain (static) methods for frequent computations that do not really fit anywhere else.
 * For example, the computation of spatial relations should not be a task of the DescribedObjects class.
 *
 */
public class GIVEUtils {

    /**
     * @author Boris Fersing
     * @return true if GIVE_DEBUG java property is 1, else otherwise
     */
    public static boolean debug() {
        if (System.getProperty("GIVE_DEBUG") != null && System.getProperty("GIVE_DEBUG").equals("1")) {
            return true;
        } else {
            return false;
        }
    }

    public static boolean highLevelNav() {
        if (System.getProperty("GIVE_NOHL") == null || System.getProperty("GIVE_NOHL").equals("0")) {
            return true;
        } else {
            return false;
        }
    }
    
        public static boolean rememberRooms() {
        if (System.getProperty("GIVE_NOREMEMBER") == null || System.getProperty("GIVE_NOREMEMBER").equals("0")) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * This method is used to compute the spatial relation between an object and a landmark, as observed by the player from their position
     *
     * @param pObject The position of the object that has some spatial relation to the landmark.
     * @param pLandmark The position of the landmark object.
     * @param pPlayer The position from which the spatial relation is observed.
     * @return the spatial relation between the object and the landmark.
     */
    public static OrientationType computeSpatialRelation(Position pObject, Position pLandmark, Position pPlayer) {

        Orientation playerLookAtLandmark = pPlayer.getOrientationTo(pLandmark);
        if (GIVEUtils.debug()) {
            System.out.println("Orientation vector from player to landmark " + playerLookAtLandmark.toString());
        }

        Orientation playerLookAtObject = pPlayer.getOrientationTo(pObject);
        if (GIVEUtils.debug()) {
            System.out.println("Orientation vector from player to obj is " + playerLookAtObject.toString());
        }

        double angle = GeometryTools.getAngle(playerLookAtObject, playerLookAtLandmark);
        // angle is represented as a value between -Math.PI and +Math.PI
        if (angle >= (0 - Math.PI) && angle < 0) {
            if (GIVEUtils.debug()) {
                System.out.println("obj is right of landmark");
            }
            return OrientationType.RIGHT;
        } else if (angle >= 0 && angle <= Math.PI) {
            if (GIVEUtils.debug()) {
                System.out.println("obj is left of landmark");
            }
            return OrientationType.LEFT;
        }
        return null;
    }

    /**
     * The method computes spatial relations between the user and the target using GeometryTools from give.util
     * GeometryTools contain methods that tell "whether the given angle would be called "forward" \ "left \ "right" by an observer that faces towards zero degrees."
     * The good thing about these methods is that they take the orientation of the target into account.
     * The bad thing about using these methods is that doors are always oriented into only one of the adjacent rooms.
     * So, sometimes, the door that is on the user's right hand side, is thought to be on its left.
     * That is why I first calculate the angle between the user facing the postion of the target and then, if the angle is positive,
     * and if the methods from the GeometryTools will consider the target to be "left" or "right", I know that it is left,
     * and if the angle is negative, I know that it is right.
     * If the methods from GeometryTools tell that the target is in front\behind, I know that it is in front,
     * because we simply don't call this method if the target is invisible, i.e. if it is behind the user.
     *
     * @param World w - the current world
     * @param String targetID - the ID of the target (i.e. "b5")
     *
     * @return OrientationType left, right, front or (in case of some mistake) behind.
     *
     * @author Olga
     *
     */
    public static OrientationType GeometryToolsSpatialRelations(World w, String targetID) {

        double angle = w.getPlayerOrientation().getAngleTo(w.getPlayerPosition().getOrientationTo(w.getPosition(targetID)));

        if (angle < 0 && GeometryTools.isRight(java.lang.Math.abs(angle))) {
            return OrientationType.RIGHT;
        } else if (angle < 0 && GeometryTools.isLeft(java.lang.Math.abs(angle))) {
            return OrientationType.RIGHT;
        } else if (angle > 0 && GeometryTools.isRight(java.lang.Math.abs(angle))) {
            return OrientationType.LEFT;
        } else if (angle > 0 && GeometryTools.isLeft(java.lang.Math.abs(angle))) {
            return OrientationType.LEFT;
        } else if (GeometryTools.isBackwards(java.lang.Math.abs(angle)) || GeometryTools.isForward(java.lang.Math.abs(angle))) {
            return OrientationType.FRONT;
        } else {
            //some error
            return OrientationType.BEHIND;
        }
    }

    /**
     * This method computes the spatial relation of an object w.r.t. the position of the player.
     * Also, the orientation of the player is taken into consideration, as they might be looking away from the object.
     *
     * @param pPlayer The player's position.
     * @param playerOrientation The player's orientation.
     * @param pLandmark The landmark's position.
     * @return The position of the landmark w.r.t to the player.
     */
    public static OrientationType computeSpatialRelationPlayerLandmark(Position pPlayer, Orientation playerOrientation, Position pLandmark) {

        Orientation playerLookAtLandmark = pPlayer.getOrientationTo(pLandmark);
        if (GIVEUtils.debug()) {
            System.out.println("Orientation vector from player to landmark " + playerLookAtLandmark.toString());
        }


        double angle = GeometryTools.getAngle(playerLookAtLandmark, playerOrientation);
        // angle is represented as a value between -Math.PI and +Math.PI
        if (GIVEUtils.debug()) {
            System.out.println("ANGLE: " + angle);
        }

        //front
        if (angle >= -0.2 && angle <= 0.2) {
            //this means that objects that are about 20° to the front left and the front right of the player are classified as "in front"
            return OrientationType.FRONT;

        } // front right
        else if (angle > 0.2 && angle <= 0.75) {
            if (GIVEUtils.debug()) {
                System.out.println("landmark is front right of player");
            }
            return OrientationType.FRONT_RIGHT;
        } // front left
        else if (angle >= -0.75 && angle < -0.2) {
            if (GIVEUtils.debug()) {
                System.out.println("landmark is front right of player");
            }
            return OrientationType.FRONT_RIGHT;
        } // left and right are now defined as about 60° (might be a little too much!)
        //Olga: I've made it smaller. NB: changes didn't affect the tests!!! Need new tests.
        //right
        else if (angle >= -1.05 && angle < -0.75) {
            if (GIVEUtils.debug()) {
                System.out.println("landmark is right of player");
            }
            return OrientationType.RIGHT;
        } //left
        else if (angle > 0.75 && angle <= 1.05) {
            if (GIVEUtils.debug()) {
                System.out.println("landmark is left of player");
            }
            return OrientationType.LEFT;
        } //behind-left
        else if (angle > 1.05 && angle <= 2.27) {
            if (GIVEUtils.debug()) {
                System.out.println("landmark is behind-left of player");
            }
            return OrientationType.BEHIND_LEFT;
        } //behind-right
        else if (angle < -1.05 && angle >= -2.27) {
            if (GIVEUtils.debug()) {
                System.out.println("landmark is behind-right of player");
            }
            return OrientationType.BEHIND_RIGHT;
        }
        //else:
        return OrientationType.BEHIND;
    }

    /**
     * This method prints messages if the GIVE_DEBUG environment variable has been set.
     */
    public static void writeDebug(String debugMessage) {
        if (GIVEUtils.debug()) {
            System.out.println(debugMessage);
        }
    }

    /**
     * @author Boris Fersing
     * This method checks if something at a position p2 is visible from a position p1.
     *
     * @param p1 Position1
     * @param p2 Position2
     * @param w The world where we have to do the check.
     * @return true if visible, false otherwise.
     */
    public static boolean checkVisibility(Position p1, Position p2, World w) {

        // The coordinates of the 2 positions.
        double p1_x = p1.getX();
        double p1_z = p1.getZ();
        double p2_x = p2.getX();
        double p2_z = p2.getZ();

        // This line represents the visibility.
        // If something intersects this line, then the target is not visible.
        Line2D visibilityLine = new Line2D.Double(p1_x, p1_z, p2_x, p2_z);

        for (Term object : w.getUniverseForTermType("object")) {
            // Check for intersection with walls
            if (w.getType(object.toString()).equalsIgnoreCase("wall")) {
                String wall = object.toString();
                Position wall_p = w.getPosition(wall);
                Orientation wall_o = w.getOrientation(wall);

                if (wall_o.getX() != 0.0) {
                    if (visibilityLine.intersectsLine(wall_p.getX(), wall_p.getZ() - 0.5, wall_p.getX(), wall_p.getZ() + 0.5)) {
                        return false;
                    }
                } else {
                    if (visibilityLine.intersectsLine(wall_p.getX() - 0.5, wall_p.getZ(), wall_p.getX() + 0.5, wall_p.getZ())) {
                        return false;
                    }
                }
            }

            // Now check for intersection with CLOSED doors
            // Make sure that the door which intersects with the visibility line is not one of the targets
            if (w.getType(object.toString()).equalsIgnoreCase("door") && w.getState(object.toString()).equalsIgnoreCase("closed")) {
                String door = object.toString();
                Position door_p = w.getPosition(door);
                Orientation door_o = w.getOrientation(door);

                if (door_o.getX() != 0.0) {
                    if (visibilityLine.intersectsLine(door_p.getX(), door_p.getZ() - 0.5, door_p.getX(), door_p.getZ() + 0.5)
                            && door_p.distance(p1) != 0.0 && door_p.distance(p2) != 0.0) {
                        return false;
                    }
                } else {
                    if (visibilityLine.intersectsLine(door_p.getX() - 0.5, door_p.getZ(), door_p.getX() + 0.5, door_p.getZ())
                            && door_p.distance(p1) != 0.0 && door_p.distance(p2) != 0.0) {
                        return false;
                    }
                }
            }
        }
        return true;
    }

    /**
     * @author Boris Fersing
     * This method checks if the target might be visible from a position p1.
     *
     * @param p1 Position1
     * @param target The target for which we want to check the visibility
     * @param w The world where we have to do the check.
     * @return true if visible, false otherwise.
     */
    public static boolean checkVisibility(Position p1, DescribedObject target, World w) {
        Position target_pos = w.getPosition(target.getId());

        return checkVisibility(p1, target_pos, w);
    }

    /* Get a list of all objects in the room the player is in as strings.*/
    public static List<String> getCurrentRoomContents(World w, Map<String, DescribedObject> describedObjects) {

        /* get the ID of the current room, then get its DescribedObject, then get its contents.
         * Note that you have to use getZ and not getY to make it work*/
        List<DescribedObject> objects;
        Room r = w.getRoomAtPosition(w.getPlayerPosition().getX(), w.getPlayerPosition().getZ());

        if (r != null) {
            objects = describedObjects.get(r.getName()).getRoomContents();
        } else {
            objects = new ArrayList<DescribedObject>();
        }

        /* now get the ID of every object, put it in a list and return the list*/
        List<String> objectNames = new ArrayList<String>();
        objectNames.clear();
        for (DescribedObject o : objects) {
            objectNames.add(o.getId());
        }

        return objectNames;
    }
    //	/**
    //	 * Finds and computes the distances of close landmarks to a given
    //	 * describedObject.
    //	 * I've put the computation of the distractor set out of this method,
    //	 * because it depends on the visibility of the target.
    //	 *
    //	 * @param d
    //	 *            A describedObject for which close landmarks will be found
    //	 * @param w
    //	 *            The world the object resides in
    //	 */

    public static HashMap<String, Double> computeCloseLandmarks(DescribedObject d, List<String> distrs) {
        // get room contents - we only want to compute close landmarks in the
        // same room!
        HashMap<String, Double> closeLandmarks = new HashMap<String, Double>();
        if (distrs.isEmpty()) {
            return closeLandmarks;
        }
        //contents = d.getWorld().getRoomContents(d.getRoom());
        if (GIVEUtils.debug()) {
            System.out.println(d.getRoom() + " contains: " + distrs.toString());
        }
        // compute distances

        for (String c : distrs) {
            // everything except walls and alarm tiles are possible landmarks
            // we also do not want to count an object as being close to itself
            // (!d.getWorld().getType(c).equalsIgnoreCase("button"))
            if (GIVEUtils.debug()) {
                System.out.println("Checking type of " + c);
            }
            if ((!d.getWorld().getType(c).equalsIgnoreCase("alarm")) && !(c.equalsIgnoreCase(d.getId()))
                    && (!d.getWorld().getType(c).equalsIgnoreCase("wall")) && !d.equals(null) && !d.equals("")) {
                if (GIVEUtils.debug()) {
                    System.out.println("Compute distance for " + c);
                }
                double dist = computeDistance(c, d.getId(), d.getWorld());
                // if a landmark is close enough, add it to the list of close
                // landmarks
                if (dist <= 1.9) {
                    // add close landmarks to the describedObject
                    // we do not want to have doors as landmarks for doors. so let's check for this case
                    if (!d.getWorld().getType(c).equalsIgnoreCase("door") && d.getType() != ObjectType.DOOR) {
                        closeLandmarks.put(c, dist);
                    }
                    if (GIVEUtils.debug()) {
                        System.out.println("Added " + c + "to the list of possible landmarks for " + d.getId());
                    }
                } else {
                    // do nothing (here was a debugging println)
                }
            } else {
                // ditto
            }
        }
        return closeLandmarks;
    }

    public static double computeDistance(String c, String d, World w) {
        double cx, cy, dx, dy;
        cx = w.getPosition(c).getX();
        cy = w.getPosition(c).getZ();
        dx = w.getPosition(d).getX();
        dy = w.getPosition(d).getZ();

        double xdist = Math.abs(cx - dx);
        double ydist = Math.abs(cy - dy);

        double dist = Math.sqrt(Math.pow(xdist, 2.0) + Math.pow(ydist, 2.0));
        return dist;
    }
    /* computes the center of a given region by creating a RegionDiscretizer and
     * getting a bounding box from it, then computing the center of that box*/

    public static Position getCenterOfRegion(RegionDiscretizer r, String region) {

        // RegionDiscretizer r = new RegionDiscretizer(w);
        RegionDiscretizer.RegionInfo Info = r.getRegionInformation(region);
        Rectangle2D box = Info.getBoundingBox();
        Position center = r.getApproximatePosition(region);
        center.setX(box.getCenterX());
        center.setZ(box.getCenterY());
        return center;
    }

    /* Compute the set of distractos
     *  We should put there:
     * all visible objects, if the target is visible.
     * all objects in the room, if the target is invisible.
     *
     * Always remove the target from the set of distractors.
     *
     * It might be useful to compute "potential visibility",
     * i.e. which objects can come into sight
     * while the user is moving from his position to the target
     * and consider only "potentially visible" objects as distractors.
     */
    public static ArrayList<String> computeDistractors(Map<String, DescribedObject> describedObjects, DescribedObject target, List<String> visibleObjects) {
        List<String> auxdistr;
        ArrayList<String> distractors;
        ArrayList<String> roomContent;
        World w = target.getWorld();

        //We will need to get the content of the room;
        roomContent = new ArrayList<String>(GIVEUtils.getCurrentRoomContents(w, describedObjects));

        //We need some positions and orientaions to compute spatial relations.
        Position playerPos = w.getPlayerPosition();

        if (visibleObjects.contains(target.getId())) {
            /* I use a constructor to make a shallow copy of visibleObjects,
             * otherwise if I remove something from distractors,
             * I remove it from visibleObjects as well.
             */
            distractors = new ArrayList<String>(visibleObjects);
            distractors.remove(target.getId());
            auxdistr = new ArrayList<String>(distractors);
            Position distrPos;
            for (String d : auxdistr) {
                distrPos = w.getPosition(d);
                if (!GIVEUtils.checkVisibility(playerPos, distrPos, w)) {
                    distractors.remove(d);
                }
            }


        } else if (!roomContent.isEmpty()) {
            //Same here: use a constructor to make a shallow copy of roomContent

            distractors = new ArrayList<String>(roomContent);
            distractors.remove(target.getId());


        } else {
            //if (roomContent.isEmpty()) {
			/* this should never happen,
             * i.e. I suppose, we don't want to get a RE for an object that is not in the room
             * but we might want this in some cases....
             */
            if (debug()) {
                System.out.println("An error: the method thinks there's nothing in the room");
            }
            distractors = new ArrayList<String>();
        }


        //Exclude walls from the distractors: they are not important right now.
        //I guess, since I want to change the set I am iterating over,
        //it's better to use a copy of it. Whatever, it seems to fix the bug.
        //Also exclude the alarm tile


        auxdistr = new ArrayList<String>(distractors);
        for (String d : auxdistr) {
            try {
                if (describedObjects.get(d).getType() == ObjectType.WALL || describedObjects.get(d).getType() == ObjectType.ALARM) {
                    distractors.remove(d);
                }
            } catch (NullPointerException npe) {
                System.out.println("!!!!!! Null pointer on : " + d);
            }
        }

        /*
         * If the target is a door, exclude doors that are not in the room from distractors
         */
        if (target.getType() == ObjectType.DOOR) {
            for (String d : auxdistr) {
                try {
                    if (!roomContent.contains(d)/* && w.getPosition(d).distance(w.getPosition(target.getId())) > 1*/) {
                        distractors.remove(d);
                    }
                } catch (NullPointerException npe) {
                    System.out.println("!!!!!! Null pointer on : " + d);
                }
            }
        }
        return distractors;
    }

    /**
     * Checks if there are some close active alarm tiles in the given room for a give world
     * @author Boris Fersing
     * @param room The room we want to check
     * @param w the corresponding world
     * @return true if some active alarm tiles are found, false otherwise
     */
    public static boolean roomContainsActiveAlarmTile(DescribedObject room, World w) {
        return false;

        /*
        if (room.getType() == ObjectType.ROOM) {
            for (DescribedObject object : room.getRoomContents()) {
                if (object.getType() == ObjectType.ALARM) {
                    if (w.getState(object.getId()) != null && w.getState(object.getId()).equalsIgnoreCase("alarmed")) {
                        return true;
                    }
                }
            }
        }
        return false;
         * 
         */
    }

    /**
     * Checks if the user sees some close active alarm tiles for a give world
     * @author Boris Fersing
     * @param visible String list of visible objects
     * @param w the corresponding world
     * @return true if some active alarm tiles are found, false otherwise
     */
    public static boolean visibleActiveAlarmTile(List<String> visible, World w) {
        return false;

        /*
        for (String object : visible) {
            if (w.getType(object).equalsIgnoreCase("alarm") && w.getState(object).equalsIgnoreCase("alarmed")) {
                if (w.getPlayerPosition().distance(w.getPosition(object)) < 5) {
                    return true;
                }
            }
        }
        return false;
         * 
         */
    }

    /**
     * Returns a set of all positions which are adjacent to a given position
     *
     * @param pos The position for which we want the adjacent positions
     * @param positions The set of all positions
     * @param r The region discretizer
     * @return Set of adjacent positions
     */
    public static Set<String> getAdjacentPositions(String pos, Collection<String> positions, RegionDiscretizer r) {
        Set<String> adjpos = new TreeSet<String>();

        for (String position : positions) {
            if (r.isAdjacent(pos, position, "")) {
                adjpos.add(position);
            }
        }
        return adjpos;
    }

    /**
     * removes positions of alarmed alarms and closed doors from a set of positions
     * @param positions a set of positions
     * @param w the give world
     * @return a set of positions (names)
     */
    public static Set<String> removeBadPositions(Collection<String> positionsCollec, World w) {
        Set<String> positions = new TreeSet();
        List<String> badPositions = new ArrayList<String>();

        for (Term object : w.getUniverseForTermType("object")) {
            // Check for intersection with walls
            if (w.getType(object.toString()).equalsIgnoreCase("alarm")
                    && w.getState(object.toString()).equalsIgnoreCase("alarmed")) {
                badPositions.add(object.toString());
            }
            if (w.getType(object.toString()).equalsIgnoreCase("door")
                    && w.getState(object.toString()).equalsIgnoreCase("closed")) {
                badPositions.add(object.toString());
            }
        }

        positionfor:
        for (String position : positionsCollec) {
            for (String badpos : badPositions) {
                if (position.contains("_" + badpos + "_")) {
                    continue positionfor;
                }
            }
            positions.add(position);
        }

        return positions;
    }

    public static HighLevelPath getShortestPaths(String pos1, String pos2, World w) {

        //Build a regionDiscretizer for the world
        RegionDiscretizer r = new RegionDiscretizer(w);

        //Create the graph
        DirectedGraph<String, DefaultEdge> directedGraph = new DefaultDirectedGraph<String, DefaultEdge>(DefaultEdge.class);

        //Remove the bad positions
        Set<String> positions = removeBadPositions(r.getAllPositionNames(), w);

        // Add the good positions to the graph
        for (String position : positions) {
            directedGraph.addVertex(position);
        }

        // Create edges between adjacent positions
        for (String position : positions) {
            for (String adjpos : getAdjacentPositions(position, positions, r)) {
                directedGraph.addEdge(position, adjpos);
            }
        }

        // Get the shortest path from pos1 to pos2
        KShortestPaths ksp = new KShortestPaths(directedGraph, pos1, 3);
        HighLevelPath path = new HighLevelPath(ksp.getPaths(pos2), w);



        return path;
    }

    /**
     * Returns a list (path) of PathRooms for a given plan
     * @param plan
     * @return
     */
    public static List<PathRoom> getPathRooms(SaarNlgSystem nlg, Map<String, DescribedObject> describedObjects) {
        World w = nlg.getWorld();
        RegionDiscretizer r = new RegionDiscretizer(w);
        List<PathRoom> rooms = new ArrayList<PathRoom>();
        Set<DescribedObject> historyRooms = new TreeSet<DescribedObject>();
        Set<DescribedObject> historyObjects = new TreeSet<DescribedObject>();

        try {
            List<Atom> plan = nlg.getPlan(w.getGoals());


            for (int i = 0; i < plan.size(); i++) {
                String currentRoomName = "";
                boolean manipulate = false;
                DescribedObject currentRoom;

                if (plan.get(i).getPredicate().equals("move") || plan.get(i).getPredicate().contains("take")) {
                    currentRoomName = r.getRegionInformation(plan.get(i).getArgumentAsString(0)).getRoom();

                    if (plan.get(i).getPredicate().contains("take")) {
                        manipulate = true;
                    }
                }

                if (plan.get(i).getPredicate().equals("manipulate")
                        || plan.get(i).getPredicate().equals("manipulate-stateless")) {
                    currentRoomName = describedObjects.get(plan.get(i).getArgumentAsString(0)).getRoom();
                    manipulate = true;
                }

                // Now set the current Room
                currentRoom = describedObjects.get(currentRoomName);

                // If the list is empty, add the room
                if (rooms.isEmpty()) {
                    historyRooms.add(currentRoom);
                    historyObjects.addAll(currentRoom.getRoomContents());
                    PathRoom room = new PathRoom(currentRoom, new TreeSet<DescribedObject>(historyRooms),
                            new TreeSet<DescribedObject>(historyObjects), manipulate);
                    rooms.add(room);
                } // if the current room is already the last room of the list, only update it
                // if we found a manipulate action in the room
                else {
                    if (rooms.get(rooms.size() - 1).getId().equals(currentRoom.getId()) && manipulate) {
                        rooms.get(rooms.size() - 1).setManipulate(manipulate);
                    } //if the current room is not the last room of the list, add it
                    else if (!rooms.get(rooms.size() - 1).getId().equals(currentRoom.getId())) {
                        historyRooms.add(currentRoom);
                        historyObjects.addAll(currentRoom.getRoomContents());
                        PathRoom room = new PathRoom(currentRoom, new TreeSet<DescribedObject>(historyRooms),
                                new TreeSet<DescribedObject>(historyObjects), manipulate);
                        rooms.add(room);
                    }
                }
            }
        } catch (IOException ioe) {
            System.out.println("Error while getting plan in GIVEUtils getPathRooms");
        }

        return rooms;
    }

    public static void setRoomAlerts(InstructionGenerator ig, List<PathRoom> pathRooms) {

        // If the plan contains less than 4 rooms we don't need to set alerts because
        // if the 3rd room is the same than the 1st one we'll use the instruction "previous room"
        if (pathRooms.size() > 3) {

            int previousmanipulateIndex = -1;

            for (int i = 0; i < pathRooms.size(); i++) {
                PathRoom currentRoom = pathRooms.get(i);
                if (currentRoom.isManipulate()) {
                    //If the last manipulate has not at least 2 rooms in its history it's not
                    // useful to go further
                    if (previousmanipulateIndex >= 2) {
                        if (currentRoom.isDistinctive()) {
                            //If the room is in the history and is not the previous room, set an alert
                            if (currentRoom.isInList(pathRooms.subList(0, previousmanipulateIndex))
                                    && !currentRoom.getId().equalsIgnoreCase(pathRooms.get(previousmanipulateIndex - 1).getId())) {
                                ig.generateAlertsForRoom(currentRoom);
                            }
                        } //The room is not distinctive, search the history
                        else {
                            for (int j = i; j > previousmanipulateIndex; j--) {
                                PathRoom historyRoom = pathRooms.get(j);

                                if (historyRoom.isDistinctive()) {
                                    if (historyRoom.isInList(pathRooms.subList(0, previousmanipulateIndex))
                                            && !historyRoom.getId().equalsIgnoreCase(pathRooms.get(previousmanipulateIndex - 1).getId())) {
                                        ig.generateAlertsForRoom(historyRoom);

                                        //We found a room, break the for loop
                                        break;
                                    }
                                }
                            }
                        }
                        //Update the previous manipulate value
                        previousmanipulateIndex = i;
                    } else {
                        //Update the previous manipulate value

                        previousmanipulateIndex = i;
                    }
                }
            }
        }
    }
}
